import StringIO
import sys

class DotWriter(object):
    """
    Usage:
        * __init__ - accepts a list of <ClassAgent> object
        * .toDot()
    """
    define = '%(node_name)s '\
             '[shape="record", '\
             'label="{%(class_name)s|'\
                   '%(attr_list)s|'\
                   '%(method_name_list)s}"];\n'
    connect = '%s -> %s;\n'
    def __init__(self, class_agent_list):
        self.class_agent_list = class_agent_list

        # define_string is used to save the class-define statements
        self.define_string = StringIO.StringIO()

        # connection_string is used to save the line informations
        self.connection_string = StringIO.StringIO()

        # TODO need a more graceful way
        self.stream = file('haha.dot', 'w')
        # we can use this hook to display the result in the console
        #self.stream = sys.stdout


    def toDot(self):
        self.stream.write('digraph hehe{\n')

        for each in self.class_agent_list:
            self._agent_to_dot(each)

        # put the cursor in the beginning 
        self.define_string.seek(0)
        self.connection_string.seek(0)

        # write down `DOT` language
        self.stream.write(self.define_string.read())
        self.stream.write(self.connection_string.read())
        self.stream.write('}')

        self.stream.close()

    def _agent_to_dot(self, agent):
        # node_name is used to `DAIBIAO` this class in output
        node_name = agent.getName().replace('.', '_')
        # label is to be displaied in the graph
        label = agent.getName()

        parent_list = agent.getParentName()

        classBoxString = self._generateClassBox(node_name, label,
                        agent.method_list, agent.attr_list)
        self.define_string.write(classBoxString)

        for each in parent_list:
            parent_name = each.replace('.', '_')
            self.connection_string.write(self.connect % (node_name, parent_name))

    def _generateClassBox(self, node_name, class_name, method_list, attr_list):
        """
        @node_name: the name of the class - person won't see this in the graph
        @class_name: the name displayed in the graph
        @method_list: a list filled with <MethodAgent> objects
        @attr_list: a list filled with <AttrAgent> objects
        """
        #if not method_list:
            #method_list = []
        # FIRST: generate method_name_list
        _method_name_list = []
        for each_method_agent in method_list:
            _method_name_list.append('%s(%s)' %
                            (each_method_agent.getName(),
                             ', '.join(each_method_agent.getParam())))

        # THEN: generate attributes list
        _attr_list = []
        for each_attr_agent in attr_list:
            _attr_list.append('%s: %s' %
                                (each_attr_agent.getName(),
                                 each_attr_agent.getType()))

        res = {'class_name': class_name,
               'node_name': node_name,
                # attributes, method
               'attr_list': '\l'.join(_attr_list) + '\l',
               'method_name_list': '\l'.join(_method_name_list) + '\l'}
        return self.define % res


